﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace EOrm.NpgsqlProvider
{
    /// <summary>
    /// postgres的where条件表达式的访问器
    /// </summary>
    public class PgWhereVisitor : WhereVisitor
    {
        /// <summary>
        /// 表达式访问器
        /// </summary>
        /// <param name="exp">表达式</param>
        /// <param name="table">要访问的数据表</param>
        /// <exception cref="Exception">表达式访问异常</exception>
        public override void Visit(Expression exp, ITable table)
        {
            //验证表达式是否为空
            if (exp == null)
            {
                return;
                //throw new Exception("不能访问空表达式");
            }
            //验证数据表
            if (table == null)
            {
                throw new Exception("数据表结构获取失败");
            }
            //根据表达式的类型处理不同的逻辑
            switch (exp.NodeType)
            {
                //and和or逻辑
                case ExpressionType.AndAlso:
                case ExpressionType.OrElse:
                    //二元表达式
                    BinaryExpression binLogicExp = exp as BinaryExpression;
                    //验证二元表达式
                    if (binLogicExp == null)
                    {
                        throw new Exception("逻辑运算向二元表达式转换失败");
                    }
                    //推入右括号
                    _whereStringStack.Push(")");
                    //访问右部
                    Visit(binLogicExp.Right, table);
                    //添加逻辑运算连接符
                    _whereStringStack.Push(" " + ChangeExpressionType(exp.NodeType) + " ");
                    //访问左部
                    Visit(binLogicExp.Left, table);
                    //压入左括号
                    _whereStringStack.Push("(");
                    break;
                //调用表达式
                case ExpressionType.Call:
                    //方法调用表达式
                    MethodCallExpression mcExp = exp as MethodCallExpression;
                    //验证调用表达式
                    if (mcExp == null)
                    {
                        throw new Exception("向方法调用表达式转换失败");
                    }
                    //获取调用名
                    string methodName = mcExp.Method.Name;
                    //针对StartsWith、EndsWith、Contains调用，主要配置like子句
                    if (methodName == "StartsWith" || methodName == "EndsWith" || methodName == "Contains")
                    {
                        //推入右括号
                        _whereStringStack.Push(")");
                        //访问首个变量
                        Visit(mcExp.Arguments[0], table);
                        //访问对象
                        Visit(mcExp.Object, table);
                        //弹出变量名
                        string likePmsName = _whereStringStack.Pop();
                        //弹出变量值
                        object likePmsVal = _paramValStack.Pop();
                        //获取数据表字段
                        IField likeField = table.Fields.Find(a => a.Name == likePmsName);
                        //字段判定
                        if (likeField == null)
                        {
                            throw new Exception(string.Format("没有找到字段{0}的定义", likePmsName));
                        }
                        //构建连接字符
                        string likeConjunctStr = "";
                        //如果上一表达式访问是NOT，则设定连接字符串为NOT LIKE，否则设定为LIKE
                        if (_isLastNot)
                        {
                            likeConjunctStr = "NOT LIKE";
                        }
                        else
                        {
                            likeConjunctStr = "LIKE";
                        }
                        //构造变量值
                        string likeValStr = "";
                        if (methodName == "StartsWith")
                        {
                            likeValStr = likePmsVal.ToString() + "%";
                        }
                        else if (methodName == "EndsWith")
                        {
                            likeValStr = "%" + likePmsVal.ToString();
                        }
                        else
                        {
                            likeValStr = "%" + likePmsVal.ToString() + "%";
                        }
                        //组合sql子句
                        string likePartialStr = string.Format("{0} {1} <%{2}%>", likeField.DbName, likeConjunctStr, likeValStr);
                        //压入sql子句
                        _whereStringStack.Push(likePartialStr);
                        //NOT表达式访问标志置为假
                        _isLastNot = false;
                        //推入左括号
                        _whereStringStack.Push("(");
                    }
                    else
                    {
                        //其余情况验证上一表达式是否为NOT，如果是NOT，抛出异常
                        if (_isLastNot)
                        {
                            throw new Exception(string.Format("兰布达表达式出错，不可以用否定符号修饰{0}子句", methodName));
                        }
                    }
                    break;
                //常量表达式的访问
                case ExpressionType.Constant:
                    //表达式转换为常量表达式
                    ConstantExpression constExp = exp as ConstantExpression;
                    //验证表达式
                    if (constExp == null)
                    {
                        throw new Exception("向常量表达式转换失败");
                    }
                    //获取常量表达式的值
                    object obj = constExp.Value;
                    //获取常量表达式值的原始对象的所有字段
                    FieldInfo[] fis = obj.GetType().GetFields();
                    //定义所有字段对应的值
                    List<object> objs = new List<object>();
                    //遍历并添加所有字段的值（这里未来可能会进行拓展）
                    foreach (FieldInfo constField in fis)
                    {
                        //优先验证值是否为直接处理的值
                        if (constField.Name != "Empty"&& constField.Name != "MaxValue" && constField.Name != "MinValue")
                        {
                            //获取对应字段的对应值，并将该值添加到字段值列表中
                            objs.Add(constField.GetValue(obj));
                        }
                        else
                        {
                            //获取字段类型名
                            string fieldTypeName = constField.FieldType.Name;
                            //如果是基础类型
                            if (fieldTypeName == "String" || fieldTypeName == "DateTime" || fieldTypeName == "Boolean" || fieldTypeName == "Int16" || fieldTypeName == "Int32" || fieldTypeName == "Int64" || fieldTypeName == "Single" || fieldTypeName == "Double" || fieldTypeName == "Decimal" || fieldTypeName == "UInt16" || fieldTypeName == "UInt32" || fieldTypeName == "UInt64" || fieldTypeName == "Byte" || fieldTypeName == "SByte")
                            {
                                //直接添加对象
                                objs.Add(obj);
                            }
                            //如果不是基础类型
                            else
                            {
                                //获取对应字段的对应值，并将该值添加到字段值列表中
                                objs.Add(constField.GetValue(obj));
                            }
                        }
                    }
                    //将首个元素返回（注意，未来拓展时此处可能跟随变化）
                    _paramValStack.Push(objs[0]);
                    break;
                //二元的比较表达式
                case ExpressionType.Equal:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.NotEqual:
                    //转换为二元比较表达式
                    BinaryExpression binCompareExp = exp as BinaryExpression;
                    //验证二元比较表达式
                    if (binCompareExp == null)
                    {
                        throw new Exception("比较运算向二元表达式转换失败");
                    }
                    //访问左部
                    Visit(binCompareExp.Left, table);
                    //访问右部
                    Visit(binCompareExp.Right, table);
                    //弹出比较变量名
                    string comparePmsName = _whereStringStack.Pop();
                    //弹出比较变量值
                    object comparePmsVal = _paramValStack.Pop();
                    //获取比较字段
                    IField compareField = table.Fields.Find(a => a.Name == comparePmsName);
                    //验证字段
                    if (compareField == null)
                    {
                        throw new Exception(string.Format("没有找到字段{0}的定义", comparePmsName));
                    }
                    //构建比较字符串
                    string compareValStr = "";
                    //获取比较变量值
                    string valTypeName = comparePmsVal.GetType().Name;
                    //如果不是基础类型
                    if (!(valTypeName == "String" || valTypeName == "DateTime" || valTypeName == "Boolean" || valTypeName == "Int16" || valTypeName == "Int32" || valTypeName == "Int64" || valTypeName == "Single" || valTypeName == "Double" || valTypeName == "Decimal" || valTypeName == "UInt16" || valTypeName == "UInt32" || valTypeName == "UInt64" || valTypeName == "Byte" || valTypeName == "SByte"))
                    {
                        //重新获取比较变量值
                        comparePmsVal = comparePmsVal.GetType().GetProperty(comparePmsName).GetValue(comparePmsVal, null);
                    }
                    //如果比较字段的为数值型
                    if (compareField.DbTypeName == "samllint" || compareField.DbTypeName == "integer" || compareField.DbTypeName == "bigint" || compareField.DbTypeName == "real" || compareField.DbTypeName == "double" || compareField.DbTypeName == "" || compareField.DbTypeName == "boolean")
                    {
                        //构建时间比较值
                        compareValStr = comparePmsVal.ToString();
                    }
                    //如果比较字段为非数值型
                    else
                    {
                        //构建时间比较字符串
                        if (comparePmsVal.GetType() == typeof(DateTime))
                        {
                            compareValStr = "<%" + ((DateTime)comparePmsVal).ToString("yyyy-MM-dd HH:mm:ss") + "%>";
                        }
                        //构建普通比较字符串
                        else
                        {
                            compareValStr = "<%" + comparePmsVal.ToString() + "%>";
                        }
                    }
                    //构建比较连接符
                    string compareConjunctStr = ChangeExpressionType(exp.NodeType);
                    //构建比较sql子句
                    string comparePartial = string.Format("{0} {1} {2}", compareField.DbName, compareConjunctStr, compareValStr);
                    //推入比较子句
                    _whereStringStack.Push(comparePartial);
                    break;
                //成员访问表达式
                case ExpressionType.MemberAccess:
                    //转换为成员表达式
                    MemberExpression memberExp = exp as MemberExpression;
                    //验证成员表达式
                    if (memberExp == null)
                    {
                        throw new Exception("向成员表达式转换失败");
                    }
                    //如果成员表达式的子表达式结点类型是变量类型
                    if (memberExp.Expression.NodeType == ExpressionType.Parameter)
                    {
                        //获取表达式的原始类型的属性信息
                        PropertyInfo pi = memberExp.Member.ReflectedType.GetProperty(memberExp.Member.Name);
                        //如果属性不存在
                        if (pi == null)
                        {
                            //表达式转换为一元表达式
                            UnaryExpression cast = Expression.Convert(memberExp, typeof(object));
                            //编译并执行获取其值
                            object memObj = Expression.Lambda<Func<object>>(cast).Compile().Invoke();
                            //将该值推送到where条件栈
                            _whereStringStack.Push(memObj.ToString());
                        }
                        //如果属性存在
                        else
                        {
                            //压入变量名
                            _whereStringStack.Push(pi.Name);
                        }
                    }
                    //如果成员表达式的子表达式结点类型不是变量类型
                    else
                    {
                        //直接访问表达式
                        Visit(memberExp.Expression, table);
                    }
                    break;
                //访问兰布达表达式
                case ExpressionType.Lambda:
                    //表达式转换为兰布达表达式
                    LambdaExpression lambdaExp = exp as LambdaExpression;
                    //验证表达式
                    if (lambdaExp == null)
                    {
                        throw new Exception("兰布达表达式为空");
                    }
                    //访问其子表达式
                    Visit(lambdaExp.Body, table);
                    break;
                //访问NOT表达式
                case ExpressionType.Not:
                    //表达式转换为一元表达式
                    UnaryExpression notUnaryExp = exp as UnaryExpression;
                    //验证一元表达式
                    if (notUnaryExp == null)
                    {
                        throw new Exception("NOT的一元表达式为空");
                    }
                    //NOT表达式连续访问处理
                    if (_isLastNot == true)
                    {
                        _isLastNot = false;
                    }
                    else
                    {
                        _isLastNot = true;
                    }
                    //访问子表达式
                    Visit(notUnaryExp.Operand, table);
                    break;
                //访问Convert表达式
                case ExpressionType.Convert:
                    //表达式转换为一元表达式
                    UnaryExpression convertUnaryExp = exp as UnaryExpression;
                    //验证一元表达式
                    if (convertUnaryExp == null)
                    {
                        throw new Exception("转换的一元表达式为空");
                    }
                    //访问子表达式
                    Visit(convertUnaryExp.Operand, table);
                    break;
                //默认的不支持表达式的异常
                default:
                    throw new Exception(string.Format("不支持的表达式类型：{0}", exp.NodeType));
            }
        }

        /// <summary>
        /// sql子句组合
        /// </summary>
        /// <returns>sql子句</returns>
        public override string Combine()
        {
            string res = string.Join("", _whereStringStack.ToArray());
            _whereStringStack.Clear();
            _paramValStack.Clear();
            _isLastNot= false;
            return res;
        }
    }
}
